#include <ctime>
#include <cstdlib>
#include <map>
#include <string>
#include "Olive.h"
#include "AnimatorHolder.h"
#include "AnimationFilmHolder.h"
#include "Platform.h"
#include "PlatformHolder.h"
#include "Timer.h"
#include "Hearts.h"
#include "HeartHolder.h"

#define LEFT_BORDER 130
#define RIGHT_BORDER 376

Olive::Olive(Dim _x, Dim _y, AnimationFilm* film, 
	FrameRangeAnimation *_walkAnimation, FrameRangeAnimator *_walkAnimator,
	FrameRangeAnimation *_dropAnimation, FrameRangeAnimator *_dropAnimator): 
	orientation(1), droppedHearts(0), Sprite(_x,_y,film),
	walkAnimation(_walkAnimation), walkAnimator(_walkAnimator),
	dropAnimation(_dropAnimation), dropAnimator(_dropAnimator) {
	frameNo = currFilm->GetTotalFrames(); 
	SetFrame(0);
	canMove = true;
	recentlyDropped = false;
	srand(time(NULL));
}

Olive::~Olive(void) {}

void Olive::GenerateNextMove(void *closure) {
	Olive *player = (Olive *) closure;

	if(HeartHolder::Flashing() || HeartHolder::spinachEnabled())
		player->DisableMovement();
	else player->EnableMovement();

	if(!player->canMove || player->droppedHearts > 24) return;
	int choice = rand()%4;

	if(player->canDropHeart()) {
		if(!player->recentlyDropped) player->DropHeart();
		else player->MoveHorizontal(player->orientation*10);
		return;
	}
	else {
		player->MoveHorizontal(player->orientation*10);
		return;
	}

	player->StopMoving();
}

bool Olive::canDropHeart(void) {
	if(this->x > ((RIGHT_BORDER+LEFT_BORDER)/2)-5 && this->x < ((RIGHT_BORDER+LEFT_BORDER)/2)+5) {
		return true;
	}
	if(this->x > LEFT_BORDER && this->x < LEFT_BORDER+8) {
		return true;
	}
	if(this->x < RIGHT_BORDER && this->x > RIGHT_BORDER-10) {
		return true;
	}
	return false;
}

void Olive::ChangeOrientation(void) {
	orientation *= -1;
}

bool Olive::CollidesWithLeftBorder(int dx) {
	if((this->x+dx) < LEFT_BORDER) 
		return true;
	return false;
}

bool Olive::CollidesWithRightBorder(int dx) {
	if ((this->x+dx) > RIGHT_BORDER)
		return true;
	return false;
}

void Olive::StopWalking(Animator *animator, void *closure) {
	Olive *player = (Olive *)closure;

	AnimatorHolder::DelayedSuspention(animator);
}

void Olive::MoveHorizontal(int dx) {
	//collision checking with borders
	if(walkAnimator->HasFinished()) {
		currFilm = (AnimationFilm *)AnimationFilmHolder::GetSingleton()->GetFilm("olive_walk");
		recentlyDropped = false;
		walkAnimator->SetOnFinish(StopWalking, (void*)this);
		walkAnimator->Start(this, walkAnimation, Timer::getgametime());
		AnimatorHolder::MarkAsRunning(walkAnimator);

		if(CollidesWithLeftBorder(dx)) {
			ChangeOrientation();
			dx = LEFT_BORDER-(this->x);
		}
		if(CollidesWithRightBorder(dx)) {
			ChangeOrientation();
			dx = (this->x)-RIGHT_BORDER;
		}
		if(dx < 0) 
			walkAnimation->SetFlipH(true);
		else
			walkAnimation->SetFlipH(false);

		walkAnimation->SetDx(dx);
	}
}

void Olive::StopMoving(void) {
	if(!walkAnimator->HasFinished()) {
		walkAnimator->Stop();
		AnimatorHolder::MarkAsSuspended(walkAnimator);
		walkAnimation->SetDx(0);
	}
}

void Olive::DropCompleted(Animator *animator, void *closure) {
	Olive *player = (Olive *)closure;

	player->droppedHearts++;
	player->recentlyDropped = true;
	player->EnableMovement();

	if(player->GetFlipH()) 
		player->SetX(player->GetX());
	AnimatorHolder::DelayedSuspention(animator);
	player->SetAnimationFilm((AnimationFilm*)AnimationFilmHolder::GetSingleton()->GetFilm("olive_walk"));
}

void Olive::DropHeart(void) {
	StopMoving();
	if(dropAnimator->HasFinished()) {
		DisableMovement();
		currFilm = (AnimationFilm *)AnimationFilmHolder::GetSingleton()->GetFilm("olive_drop_heart");
		if(GetFlipH()) 
			SetX(GetX());
		dropAnimator->SetOnFinish(DropCompleted, (void*)this);
		dropAnimator->Start(this, dropAnimation, Timer::getgametime());
		AnimatorHolder::MarkAsRunning(dropAnimator);

		HeartHolder::NewHeart(this->GetX()+10, this->GetY()-20, 
			(AnimationFilm *)AnimationFilmHolder::GetSingleton()->GetFilm("hearts"), droppedHearts);
	}
}

void Olive::Move(int dx, int dy) {

	Sprite::Move(dx, dy);
}
